/*!
  \file gsd_cplane.c
 
  \brief OGSF library - manipulating surfaces (lower level functions)
 
  GRASS OpenGL gsurf OGSF Library 
 
  (C) 1999-2008 by the GRASS Development Team
 
  This program is free software under the 
  GNU General Public License (>=v2). 
  Read the file COPYING that comes with GRASS
  for details.
  
  \author Bill Brown USACERL, GMSL/University of Illinois (January 1993)
  \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
*/

#include <grass/gstypes.h>
#include "rowcol.h"

static void init_cplane(void);

static float Cp_pt[4], Cp_norm[MAX_CPLANES][4];
static float Cp_trans[MAX_CPLANES][3], Cp_rot[MAX_CPLANES][3];
static int Cp_ison[MAX_CPLANES];	/* also need isdef? */

static void init_cplane(void)
{
    int i;

    gs_get_datacenter(Cp_pt);
    gs_get_data_avg_zmax(&(Cp_pt[Z]));

    for (i = 0; i < MAX_CPLANES; i++) {
	Cp_ison[i] = 0;
	Cp_norm[i][X] = 1.0;
	Cp_norm[i][Y] = Cp_norm[i][Z] = 0.0;
	Cp_norm[i][W] = 1.;
	Cp_rot[i][X] = Cp_trans[i][X] = 0.0;
	Cp_rot[i][Y] = Cp_trans[i][Y] = 0.0;
	Cp_rot[i][Z] = Cp_trans[i][Z] = 0.0;
    }

    return;
}

/*!
  \brief Define cplace

  \param num
  \param pt
  \param norm
*/
void gsd_def_cplane(int num, float *pt, float *norm)
{
    float sx, sy, sz, ppt[3];
    double params[4];
    float zmin, zmax;

    GS_get_scale(&sx, &sy, &sz, 1);

    /* Something's still wrong with the zexag - DONT USE TILT */
    GS_get_zrange(&zmin, &zmax, 0);

    ppt[0] = (pt[0] + Cp_pt[0]) * sx;
    ppt[1] = (pt[1] + Cp_pt[1]) * sy;
    ppt[2] = (pt[2] + Cp_pt[2] - zmin) * sz;

    params[0] = norm[0] * sx;
    params[1] = norm[1] * sy;
    params[2] = norm[2] * sz;
    GS_dv3norm(params);
    params[3] = -ppt[0] * params[0] - ppt[1] * params[1] - ppt[2] * params[2];

    gsd_def_clipplane(num, params);

    return;
}

/*!
  \brief Update cplaces

  Called when viewing matrix changes
*/
void gsd_update_cplanes(void)
{
    int i;

    for (i = 0; i < MAX_CPLANES; i++) {
	if (Cp_ison[i]) {
	    gsd_def_cplane(i, Cp_trans[i], Cp_norm[i]);
	}
    }

    return;
}

/*!
  \brief ADD
  
  \param num
*/
void gsd_cplane_on(int num)
{
    static int first = 1;

    if (first) {
	first = 0;
	init_cplane();
	gsd_def_cplane(num, Cp_trans[num], Cp_norm[num]);
    }

    gsd_set_clipplane(num, 1);

    Cp_ison[num] = 1;

    return;
}

/*!
  \brief Turn off clip plane

  \param num cplane id
*/
void gsd_cplane_off(int num)
{

    gsd_set_clipplane(num, 0);
    Cp_ison[num] = 0;

    return;
}

/*!
  \brief Get cplane state
  
  <i>onstate</i> MUST be big enough to hold MAX_CPLANES ints

  \param onstate
*/
void gsd_get_cplanes_state(int *onstate)
{
    int i;

    for (i = 0; i < MAX_CPLANES; i++) {
	onstate[i] = Cp_ison[i];
    }

    return;
}

/*!
  \brief Get cplaces

  Planes MUST be big enough to hold MAX_CPLANES Point4s

  \param places surface coordinates, normal pointing away from visible side

  \return ADD
*/
int gsd_get_cplanes(Point4 * planes)
{
    int i, ons;
    Point3 thru;

    for (ons = i = 0; i < MAX_CPLANES; i++) {
	if (Cp_ison[i]) {
	    thru[X] = Cp_pt[X] + Cp_trans[ons][X];
	    thru[Y] = Cp_pt[Y] + Cp_trans[ons][Y];
	    thru[Z] = Cp_pt[Z] + Cp_trans[ons][Z];
	    planes[ons][X] = -Cp_norm[ons][X];
	    planes[ons][Y] = -Cp_norm[ons][Y];
	    planes[ons][Z] = -Cp_norm[ons][Z];
	    planes[ons][W] = -(DOT3(planes[ons], thru));
	    ons++;
	}
    }

    return (ons);
}

/*!
  \brief ADD

  \param num
*/
void gsd_update_cpnorm(int num)
{
    float v[1][4];

    v[0][X] = v[0][W] = 1.0;
    v[0][Y] = v[0][Z] = 0.0;

    P_pushmatrix();
    P_rot(Cp_rot[num][Z], 'z');
    P_rot(Cp_rot[num][Y], 'y');
    P_rot(Cp_rot[num][X], 'x');
    P_transform(1, v, &Cp_norm[num]);
    P_popmatrix();

    return;
}

/*!
  \brief ADD

  \param num
  \param rx,ry,rz
*/
void gsd_cplane_setrot(int num, float rx, float ry, float rz)
{
    Cp_rot[num][X] = rx;
    Cp_rot[num][Y] = ry;
    Cp_rot[num][Z] = rz;

    gsd_update_cpnorm(num);
    gsd_def_cplane(num, Cp_trans[num], Cp_norm[num]);

    return;
}

/*!
  \brief ADD

  \param num
  \param tx,ty,tz
*/
void gsd_cplane_settrans(int num, float tx, float ty, float tz)
{
    Cp_trans[num][X] = tx;
    Cp_trans[num][Y] = ty;
    Cp_trans[num][Z] = tz;

    gsd_def_cplane(num, Cp_trans[num], Cp_norm[num]);

    return;
}

/*!
  \brief ADD

  \param surf1 first surface (geosurf)
  \param surf2 second surface (geosurf) [unused]
  \param cpnum
*/
void gsd_draw_cplane_fence(geosurf * surf1, geosurf * surf2, int cpnum)
{
    int was_on;
    float len, dir[3], bgn[2], end[2], px, py, fencenorm[3];

    /* temporarily turn this plane off */
    if ((was_on = Cp_ison[cpnum])) {
	gsd_set_clipplane(cpnum, 0);
    }

    /* line on surface (asuming NO TILT) is (-A,B)->(A,-B), 
       extended thru Cp_pt */
    dir[X] = -Cp_norm[cpnum][Y];
    dir[Y] = Cp_norm[cpnum][X];
    dir[Z] = 0.0;
    GS_v3norm(dir);
    px = Cp_trans[cpnum][X] + Cp_pt[X];
    py = Cp_trans[cpnum][Y] + Cp_pt[Y];

    /* TODO: make line from point & direction, clip to region */
    /* for now, to test: */
    bgn[X] = px;
    bgn[Y] = py;
    end[X] = px;
    end[Y] = 3 * VROW2Y(surf1, 0);
    len = GS_P2distance(bgn, end) - 1;
    bgn[X] = px + len * dir[X];
    bgn[Y] = py + len * dir[Y];
    end[X] = px - len * dir[X];
    end[Y] = py - len * dir[Y];

    fencenorm[X] = -Cp_norm[cpnum][X];
    fencenorm[Y] = -Cp_norm[cpnum][Y];
    fencenorm[Z] = -Cp_norm[cpnum][Z];
    gsd_wall(bgn, end, fencenorm);

    /* turn this plane back on */
    if (was_on) {
	gsd_set_clipplane(cpnum, 1);
    }

    return;
}

/*!
  \brief Draw cplane

  \param num
*/
void gsd_draw_cplane(int num)
{
    float size, cpv[3];
    int i;
    float scalez;
    unsigned long colr;

    /* turn off all clipping planes */
    for (i = 0; i < MAX_CPLANES; i++) {
	if (Cp_ison[i]) {
	    gsd_set_clipplane(i, 0);
	}
    }

    GS_get_longdim(&size);
    size /= 2.;
    cpv[X] = 0.0;

    gsd_blend(1);

    gsd_zwritemask(0x0);

    gsd_pushmatrix();

    gsd_do_scale(1);

    gsd_translate(Cp_pt[X] + Cp_trans[num][X],
		  Cp_pt[Y] + Cp_trans[num][Y], Cp_pt[Z] + Cp_trans[num][Z]);

    gsd_rot(Cp_rot[num][Z], 'z');
    gsd_rot(Cp_rot[num][Y], 'y');
    gsd_rot(Cp_rot[num][X], 'x');

    if ((scalez = GS_global_exag())) {
	gsd_scale(1.0, 1.0, 1. / scalez);
    }

    colr = (GS_default_draw_color() | 0xff000000) & 0x33ffffff;
    gsd_color_func(colr);
    gsd_bgnpolygon();
    cpv[Y] = size;
    cpv[Z] = size;
    gsd_vert_func(cpv);
    cpv[Y] = -size;
    gsd_vert_func(cpv);
    cpv[Z] = -size;
    gsd_vert_func(cpv);
    cpv[Y] = size;
    gsd_vert_func(cpv);
    gsd_endpolygon();

    gsd_popmatrix();
    gsd_blend(0);
    gsd_zwritemask(0xffffffff);

    /* turn on clipping planes */
    for (i = 0; i < MAX_CPLANES; i++) {
	if (Cp_ison[i]) {
	    gsd_set_clipplane(i, 1);
	}
    }

    return;
}
